home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-09-28 | 10.8 KB | 396 lines | [TEXT/PJMM] |
- { This unit contains the function 'fft' which, together with 350 additional }
- { programs useful in scientific work, is found in the book 'Numerical Recipes: }
- { The Art of Scientific Computing', available from Cambridge University Press. }
- { It is used here by permission of the authors. }
-
- {************************************************************************************}
- { FFT.p }
- { }
- { }
- { Version 26.9.94 }
- {************************************************************************************}
-
-
- unit user;
-
- interface
- uses
- proFit_interface;
-
- procedure main (selector: integer; pb: ExtModulesParamBlockPtr);
-
-
-
- implementation
-
- { note: MPW users must make sure that the procedure main is at the beginning of the compiled code }
- { under Think Pascal, this is cared for by the compiler }
- { We let main call a function mainMain to make sure that the code starts with a jump to }
- { our entry point even when compiling under MPW Pascal }
-
- procedure mainMain (selector: integer; pb: ExtModulesParamBlockPtr);
- forward;
- procedure main (selector: integer; pb: ExtModulesParamBlockPtr);
-
- begin
- mainMain(selector, pb);
- end;
-
-
-
-
- {--------------------------------------------------------------------------------------------------------}
-
-
-
- type
- data = array[1..8192] of extended;
- dataptr = ^data;
-
-
- procedure WriteStringAndNumber (s: str255; num: longint);
- begin
- WriteString(s);
- NumToString(num, s);
- WritelnString(s);
- end; { WriteStringAndNumber }
-
-
- procedure fft (x: dataptr; num: integer; inverse: boolean);
- var
- ii, jj, n, mmax, m, j, istep, i: integer;
- wtemp, wr, wpr, wpi, wi, theta, tempr, tempi: extended;
- begin
- n := num;
- j := 1;
- for ii := 1 to (n div 2) do
- begin
- i := 2 * ii - 1;
- if j > i then
- begin
- tempr := x^[j];
- tempi := x^[j + 1];
- x^[j] := x^[i];
- x^[j + 1] := x^[i + 1];
- x^[i] := tempr;
- x^[i + 1] := tempi;
- end;
- m := n div 2;
- while ((m >= 2) & (j > m)) do
- begin
- j := j - m;
- m := m div 2;
- end;
- j := j + m;
- if TestStop then { test if proFit or a user tries to stop the program }
- exit(fft);
- end; { for ii := 1 to (n div 2) do }
-
- mmax := 2;
- while n > mmax do
- begin
- istep := 2 * mmax;
- theta := 6.283185307959 / mmax;
- if inverse then
- theta := -theta;
- wpr := -2 * sqr(sin(0.5 * theta));
- wpi := sin(theta);
- wr := 1;
- wi := 0;
- for ii := 1 to (mmax div 2) do
- begin
- m := 2 * ii - 1;
- for jj := 0 to ((n - m) div istep) do
- begin
- i := m + jj * istep;
- j := i + mmax;
- tempr := wr * x^[j] - wi * x^[j + 1];
- tempi := wr * x^[j + 1] + wi * x^[j];
- x^[j] := x^[i] - tempr;
- x^[j + 1] := x^[i + 1] - tempi;
- x^[i] := x^[i] + tempr;
- x^[i + 1] := x^[i + 1] + tempi;
- end; { for jj := 0 to ((n - m) div istep) do }
- wtemp := wr;
- wr := wr + wr * wpr - wi * wpi;
- wi := wi + wi * wpr + wtemp * wpi;
- if TestStop then { test if proFit or a user tries to stop the program }
- exit(fft);
- end; { for ii := 1 to (mmax div 2) do }
- mmax := istep;
- end; { while n>mmax }
- end; { fft }
-
- {-----------------------------------------}
-
- procedure shiftFFT (x: dataptr; np: integer);
- var
- xx: extended;
- i, np2: integer;
- begin
- for i := 1 to np do
- begin
- xx := x^[i];
- x^[i] := x^[i + np];
- x^[i + np] := xx;
- end;
- end; { shiftFFT }
-
-
-
-
-
-
- {************************************************************************************}
-
- procedure SetUp (var moduleKind: integer; { set moduleKind to isFunction or isProgram }
- var name: Str255; { the name of the program or function }
- var requiredGlobals: longint; { the number of bytes to be allocated in ExtModulesParamBlock.globals }
- { set requiredGlobals to 0 if you don't use this feature }
- pb: ExtModulesParamBlockPtr); { the complete parameter block passed by pro Fit to the }
- { routines defined in this file. In most cases it can be ignored }
- { SetUp is called once when the external module is linked to pro Fit }
- begin
- moduleKind := isProgram;
- name := 'Complex FFT';
- requiredGlobals := 0;
- end;
-
-
-
-
- {************************************************************************************}
-
- procedure InitializeProg (pb: ExtModulesParamBlockPtr);
- { Can be left emtpy if not needed. }
- { called when the external module is linked to proFit after SetUp was called }
- { can be used to inititialize global variables, etc. }
- begin
- pb^.V[1] := 0; {used as a flag to find out when the FFT program is called for the first time}
- pb^.V[2] := 64;{ the default input parameters}
- pb^.V[3] := 1;
- pb^.V[4] := 4;
- pb^.V[5] := 0;
- pb^.V[6] := 1;
-
- end;
-
-
-
-
-
- {************************************************************************************}
-
- procedure Run (pb: ExtModulesParamBlockPtr);
- { pro Fit calls this function when the name of the program is chosen from the }
- { Run Program submenu in the menu Calc }
- var
- s1, s2, s3, s4, s5, st: str255;
- r: inputRec;
- ex1, ex2, ex3, ex4, ex5, x0, xinc, y0, yinc, yoff: extended;
- i, xCol, yCol: integer;
- x: dataptr; { pointer to the data array }
- nrData: integer; { the number of data (integer power of 2) }
- inverse: boolean; { false if normal FFT should be done, true for reverse FFT }
- answer: integer;
-
- procedure CleanUpRun;
- begin
- StopExecution;
- if x <> nil then
- DisposPtr(pointer(x)); { we don't need the memory any more }
- exit(Run); { terminate this call of run }
- end;
-
- begin
- if pb^.V[1] = 0 then
- begin { for the first time this program runs }
- pb^.V[1] := 1;
- if AskBox(answer, 'Do you want to print some information about this program to the Results window ?') then
- exit(Run) { stop button pressed }
- else if answer = 1 then { yes button pressed }
- begin
- WritelnString('The number of data points must be between 4 and 4096.');
- WritelnString('(The program sets the number of data points to the next smaller power of 2)');
- WritelnString('"First column No of data" means the column number where the "time" values are stored.');
- WritelnString('The real and imaginary values of your data must be in the succeeding two columns.');
- WritelnString('"First column No of FFT" means the column number where the "frequency" values will be stored.');
- WritelnString('The real and imaginary part of the Fourier transformed data will be in the succeeding two columns.');
- WritelnString('"Offset of FFT" defines an additive constant for the frequency values.');
- exit(Run);
- end;
- end;
-
- ex1 := pb^.v[2]; { do dialog about dataNr, ColumnNrs }
- s1 := 'Number of data points';
- r[1].x := pointer(@ex1);
- r[1].s := pointer(@s1);
- ex2 := pb^.v[3];
- s2 := 'First column No. of data';
- r[2].x := pointer(@ex2);
- r[2].s := pointer(@s2);
- ex3 := pb^.v[4];
- s3 := 'First column No of FFT';
- r[3].x := pointer(@ex3);
- r[3].s := pointer(@s3);
- ex4 := pb^.v[5];
- s4 := ' Offset of FFT';
- r[4].x := pointer(@ex4);
- r[4].s := pointer(@s4);
- ex5 := pb^.v[6];
- s5 := 'FFT (>0) ; inverse FFT (<0) ?';
- r[5].x := pointer(@ex5);
- r[5].s := pointer(@s5);
- if InputBox(5, r) then
- exit(Run) { if something is not ok we quit }
- else
- begin { if everything is ok we do something }
- ex1 := abs(ex1);
- if (ex1 > 4096) | (ex1 < 4) then
- exit(Run);
- nrData := round(exp(ln(2) * trunc(ln(ex1) / ln(2))));
- ex2 := abs(ex2);
- if (ex2 > 97) | (ex2 < 1) then
- exit(Run);
- xCol := round(ex2);
- ex3 := abs(ex3);
- if (ex3 > 97) | (ex3 < 1) then
- exit(Run);
- yCol := round(ex3);
- yoff := ex4;
- if ex5 >= 0 then
- inverse := false
- else
- inverse := true;
- pb^.v[2] := nrData;{set the default parameter to the chosen values, for the next time FFT is called}
- pb^.v[3] := ex2;
- pb^.v[4] := ex3;
- pb^.v[5] := ex4;
- if inverse then
- pb^.v[6] := -1
- else
- pb^.v[6] := 1;
-
-
-
- x := dataptr(newPtr(sizeof(extended) * longint(2) * nrData)); { prepare memory for data }
- if x = nil then
- exit(run);
-
-
- if TestData(1, xCol) then
- begin
- x0 := GetData(1, xCol);
- if TestData(2, xCol) then
- xinc := GetData(2, xCol) - x0;
- end;
-
- for i := 1 to NrData do { read data from window }
- begin
- if TestData(i, xCol + 1) then { test if data value is ok }
- x^[2 * i - 1] := GetData(i, xCol + 1)
- else
- x^[2 * i - 1] := 0;
- if TestData(i, xCol + 2) then
- x^[2 * i] := GetData(i, xCol + 2)
- else
- x^[2 * i] := 0;
- if TestStop then
- CleanUpRun;
- end;
-
- fft(x, 2 * NrData, inverse); { fft calculations }
- yinc := 1 / NrData / xinc;
- if not inverse then
- begin
- shiftFFT(x, NrData);
- y0 := yoff - 1 / xinc / 2;
- end
- else
- begin
- y0 := yoff;
- for i := 1 to NrData div 2 do
- begin
- x^[4 * i - 3] := x^[4 * i - 3] / NrData;
- x^[4 * i - 2] := x^[4 * i - 2] / NrData;
- x^[4 * i - 1] := -x^[4 * i - 1] / NrData;
- x^[4 * i] := -x^[4 * i] / NrData;
- end;
- end;
- if TestStop then
- CleanUpRun;
-
- SetColName(yCol, 'frequency');
- SetColName(yCol + 1, 'FFT real');
- SetColName(yCol + 2, 'FFT imag.');
- for i := 1 to NrData do { output of data into window }
- begin
- SetData(i, yCol, y0 + (i - 1) * yinc);
- SetData(i, yCol + 1, x^[2 * i - 1]);
- SetData(i, yCol + 2, x^[2 * i]);
- if TestStop then
- CleanUpRun;
- end;
-
- if x <> nil then
- DisposPtr(Ptr(x));{ we don't need the memory any more }
- end;
- end;{Run}
-
-
-
-
-
-
-
- {************************************************************************************}
-
- procedure CleanUp (pb: ExtModulesParamBlockPtr);
- { called when the function or program is removed from pro Fit's menus }
- { in most cases, this function can be empty }
- begin
- end;
-
-
-
-
-
-
-
-
- {***********************************************************************************************}
-
- { This is the main procedure through which all calls to the external module go. }
- { Main takes care of calling the right procedure with the right parameters depending on }
- { the value of "selector". }
- { You don't need to touch this procedure }
- procedure mainMain (selector: integer; pb: ExtModulesParamBlockPtr);
- begin
- Startup(pb);
- case selector of
- kSetup:
- begin
- pb^.requiredGlobals := 0;
- pb^.versionNumber := VERSIONNUMBER;
- if sizeof(extended) = 10 then
- pb^.codeType := CPU68noFPU
- else if sizeof(extended) = 12 then
- pb^.codeType := CPU68FPU
- else
- pb^.codeType := CPUPowerPC;
-
- SetUp(pb^.moduleKind, pb^.name, pb^.requiredGlobals, pb);
- end;
- progInitialize:
- InitializeProg(pb);
- progRun:
- Run(pb);
- kCleanUp:
- CleanUp(pb);
- otherwise
- end;
- end;
-
-
-
- end.